package com.lp.biz.order.service.impl;

import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.github.pagehelper.PageHelper;
import com.lp.biz.content.mapper.BizCoreContentMemberRelMapper;
import com.lp.biz.content.mapper.BizCourseCommentLikeMapper;
import com.lp.biz.content.mapper.BizCourseCommentMapper;
import com.lp.biz.content.model.entity.BizCoreContentMemberRel;
import com.lp.biz.content.model.entity.BizCourseComment;
import com.lp.biz.content.model.entity.BizCourseCommentLike;
import com.lp.biz.coupon.mapper.BizCouponMemberRelMapper;
import com.lp.biz.coupon.model.entity.BizCouponMemberRel;
import com.lp.biz.member.mapper.MemberMapper;
import com.lp.biz.member.model.entity.Member;
import com.lp.biz.members.mapper.BizMembersRelMapper;
import com.lp.biz.members.model.entity.BizMembersRel;
import com.lp.biz.message.service.BizMessageSystemService;
import com.lp.biz.order.mapper.WechatRefundFailLogMapper;
import com.lp.biz.order.mapper.WechatRefundMapper;
import com.lp.biz.order.mapper.WechatTransactionNotifyMapper;
import com.lp.biz.order.model.entity.BizOrder;
import com.lp.biz.order.mapper.BizOrderMapper;
import com.lp.biz.order.model.entity.WechatRefund;
import com.lp.biz.order.model.entity.WechatRefundFailLog;
import com.lp.biz.order.model.entity.WechatTransactionNotify;
import com.lp.biz.order.model.query.OrderListQuery;
import com.lp.biz.order.model.vo.OrderExcel;
import com.lp.biz.order.model.vo.OrderList;
import com.lp.biz.order.model.vo.OrderRefund;
import com.lp.biz.order.service.BizOrderService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.lp.biz.train.mapper.*;
import com.lp.biz.train.model.entity.*;
import com.lp.common.component.RedisService;
import com.lp.common.context.ContextHolder;
import com.lp.common.exception.BaseException;
import com.lp.common.exception.ErrorCodeEnum;
import com.lp.common.utils.CommonResult;
import com.lp.common.utils.ExcelUtils;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.service.partnerpayments.app.model.Transaction.TradeStateEnum;
import com.wechat.pay.java.service.refund.RefundService;
import com.wechat.pay.java.service.refund.model.Amount;
import com.wechat.pay.java.service.refund.model.AmountReq;
import com.wechat.pay.java.service.refund.model.CreateRequest;
import com.wechat.pay.java.service.refund.model.Refund;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import static com.lp.common.constant.CacheConstant.BIZ_USER_DETAIL_KEY;
import static com.lp.common.constant.CacheConstant.MEMBER_INFO_KEY;
import static com.lp.common.constant.CommonConstant.NEGATE;
import static com.lp.common.constant.CommonConstant.SURE;
import static com.lp.common.constant.CouponConstant.STATUS_NOT_USED;
import static com.lp.common.constant.IdentityConstant.STUDENT;
import static com.lp.common.constant.OrderConstant.*;
import static com.lp.common.utils.Tools.checkOrderTitle;
import static com.lp.common.utils.Tools.stripTrailingZeros;
import static com.wechat.pay.java.service.refund.model.Status.PROCESSING;
import static com.wechat.pay.java.service.refund.model.Status.SUCCESS;

/**
 * @program: backend
 * @description: BizOrder 服务实现类
 * @author Ke.Song
 * @since 2024-05-26 11:00:59
 */
@Slf4j
@Service
public class BizOrderServiceImpl extends ServiceImpl<BizOrderMapper, BizOrder> implements BizOrderService {

    @Autowired
    private RedisService redis;

    @Autowired
    private MemberMapper memberMapper;

    @Autowired
    private BizOrderMapper orderMapper;

    @Autowired
    private RefundService refundService;

    @Autowired
    private BizTrainTaskMapper taskMapper;

    @Autowired
    private BizTrainClassMapper classMapper;

    @Autowired
    private BizMembersRelMapper membersRelMapper;

    @Autowired
    private WechatRefundMapper wechatRefundMapper;

    @Autowired
    private BizTrainClassVoteMapper classVoteMapper;

    @Autowired
    private BizCourseCommentMapper courseCommentMapper;

    @Autowired
    private BizTrainTaskModeratorMapper moderatorMapper;

    @Autowired
    private BizMessageSystemService messageSystemService;

    @Autowired
    private BizCouponMemberRelMapper couponMemberRelMapper;

    @Autowired
    private BizTrainTaskMemberRelMapper taskMemberRelMapper;

    @Autowired
    private BizTrainClassMemberRelMapper classMemberRelMapper;

    @Autowired
    private BizCourseCommentLikeMapper courseCommentLikeMapper;

    @Autowired
    private WechatRefundFailLogMapper wechatRefundFailLogMapper;

    @Autowired
    private BizTrainHomeworkCommentMapper homeworkCommentMapper;

    @Autowired
    private BizCoreContentMemberRelMapper contentMemberRelMapper;

    @Autowired
    private WechatTransactionNotifyMapper transactionNotifyMapper;

    @Autowired
    private BizTrainClassMemberManageMapper classMemberManageMapper;

    @Override
    @Transactional(rollbackFor = RuntimeException.class)
    public void orderRefund(Integer orderId, OrderRefund params) {
        Integer memberId = ContextHolder.getContext().getMemberId();
        BizOrder order = orderMapper.selectById(orderId);
        if (order == null) {
            throw new BaseException(ErrorCodeEnum.PARAMS_ERROR);
        } else if (!(order.getStatus().equals(STATUS_PAID) || order.getStatus().equals(STATUS_REFUND_FAIL))) {
            throw new BaseException(ErrorCodeEnum.PARAMS_ERROR);
        }
        params.setCommissionCharge(params.getCommissionCharge()== null ? BigDecimal.ZERO : params.getCommissionCharge());
        if (order.getPayAt().plusDays(3).toLocalDate().isBefore(LocalDate.now())) {
            if (order.getRemainingPrice().subtract(params.getPrice()).subtract(params.getCommissionCharge()).compareTo(BigDecimal.ZERO) < 0) {
                throw new BaseException("退款金额错误");
            }
        } else {
            if (order.getPayPrice().subtract(params.getPrice()).subtract(params.getCommissionCharge()).compareTo(BigDecimal.ZERO) < 0) {
                throw new BaseException("退款金额错误");
            }
        }
        order.setCommissionCharge(params.getCommissionCharge());
        order.setRefundPrice(params.getPrice());
        order.setRefundReason(params.getReason());
        order.setRefundOperatorId(memberId);
        order.setRefundAt(LocalDateTime.now());
        refund(order);
        if (order.getOrderType().equals(ORDER_TYPE_MEMBERS)) {
            rollbackMembers(order);
        } else if (order.getOrderType().equals(ORDER_TYPE_TRAINING)) {
            rollbackTrain(order);
        } else if (order.getOrderType().equals(ORDER_TYPE_COURSE)) {
            rollbackCourse(order);
        }
        orderMapper.updateById(order);
    }

    @Override
    public CommonResult orderList(OrderListQuery query) {
        PageHelper.startPage(query.getPage(), query.getSize());
        List<OrderList> orderLists = orderMapper.orderList(query);
        for (OrderList orderList : orderLists) {
            orderList.setTitle(checkOrderTitle(orderList.getOrderType(), orderList.getMembersType(), orderList.getTitle()));
            orderList.setCommissionCharge(stripTrailingZeros(orderList.getCommissionCharge()));
            orderList.setCouponCharge(stripTrailingZeros(orderList.getCouponCharge()));
            orderList.setKickbackCharge(stripTrailingZeros(orderList.getKickbackCharge()));
        }
        return CommonResult.restPage(orderLists);
    }

    @Override
    public CommonResult getOrderDetail(Integer orderId) {
        return CommonResult.success(orderMapper.getOrderDetail(orderId));
    }

    @Override
    public void exportOrders(OrderListQuery query, HttpServletResponse response) {
        List<OrderExcel> excelList = new ArrayList<>();
        for (OrderList vo : orderMapper.orderList(query)) {
            excelList.add(new OrderExcel(vo));
        }
        ExcelUtils.writeExcel(response, "订单列表", "Orders", excelList, OrderExcel.class);
    }

    private void refund(BizOrder order) {
        // 根据订单ID和交易状态查询微信交易通知
        WechatTransactionNotify success = transactionNotifyMapper.selectOne(new QueryWrapper<WechatTransactionNotify>()
                .lambda().eq(WechatTransactionNotify::getOrderId, order.getId())
                .eq(WechatTransactionNotify::getTradeState, TradeStateEnum.SUCCESS));
        // 如果查询不到对应的支付成功通知，则抛出参数错误异常
        if (success == null) {
            throw new BaseException(ErrorCodeEnum.PARAMS_ERROR);
        }
        // 根据支付通知创建退款请求
        CreateRequest createRequest = getCreateRequest(success);
        WechatRefundFailLog wechatRefundFailLog = null;
        try {
            // 执行退款操作
            Refund refund = refundService.create(createRequest);
            // 根据退款结果更新订单状态
            if (refund.getStatus().equals(SUCCESS) || refund.getStatus().equals(PROCESSING)) {
                order.setStatus(STATUS_REFUND_SUCCESS);
                if (!order.getPayAt().plusDays(3).toLocalDate().isBefore(LocalDate.now())) {
                    Member member = memberMapper.selectById(order.getMemberId());
                    if (member.getInvitation() != null) {
                        messageSystemService.lowerLevelRefund(member.getInvitation(), member.getNickName());
                    }
                }
                if (order.getBizCouponId() != null) {
                    couponMemberRelMapper.update(null, new UpdateWrapper<BizCouponMemberRel>().lambda()
                            .eq(BizCouponMemberRel::getId, order.getBizCouponId())
                            .set(BizCouponMemberRel::getStatus, STATUS_NOT_USED)
                            .set(BizCouponMemberRel::getUpdateAt, LocalDateTime.now()));
                }
            } else {
                order.setStatus(STATUS_REFUND_FAIL);
                wechatRefundFailLog = new WechatRefundFailLog();
                wechatRefundFailLog.setBizOrderId(order.getId());
                wechatRefundFailLog.setReason(refund.getStatus().name());
            }
            // 生成微信退款信息并插入数据库
            WechatRefund wechatRefund = generateWechatRefund(refund);
            wechatRefundMapper.insert(wechatRefund);
        } catch (ServiceException e) {
            wechatRefundFailLog = new WechatRefundFailLog();
            wechatRefundFailLog.setBizOrderId(order.getId());
            wechatRefundFailLog.setReason(e.getErrorMessage());
            order.setStatus(STATUS_REFUND_FAIL);
        } catch (Exception e) {
            wechatRefundFailLog = new WechatRefundFailLog();
            wechatRefundFailLog.setBizOrderId(order.getId());
            wechatRefundFailLog.setReason(e.getMessage());
            order.setStatus(STATUS_REFUND_FAIL);
        }
        if (wechatRefundFailLog != null) {
            wechatRefundFailLogMapper.insert(wechatRefundFailLog);
        }
    }


    private static CreateRequest getCreateRequest(WechatTransactionNotify success) {
        CreateRequest createRequest = new CreateRequest();
        createRequest.setTransactionId(success.getTransactionId());
        createRequest.setOutTradeNo(success.getOrderNum());
        createRequest.setOutRefundNo(success.getOrderNum());
        AmountReq amountReq = new AmountReq();
        amountReq.setRefund(Long.valueOf(success.getPayerTotal()));
        amountReq.setTotal(Long.valueOf(success.getTotal()));
        amountReq.setCurrency(success.getPayerCurrency());
        createRequest.setAmount(amountReq);
        return createRequest;
    }

    private WechatRefund generateWechatRefund(Refund refund) {
        WechatRefund wechatRefund = new WechatRefund();
        wechatRefund.setRefundId(refund.getRefundId());
        wechatRefund.setOutRefundNo(refund.getOutRefundNo());
        wechatRefund.setTransactionId(refund.getTransactionId());
        wechatRefund.setOutTradeNo(refund.getOutTradeNo());
        wechatRefund.setUserReceivedAccount(refund.getUserReceivedAccount());
        wechatRefund.setSuccessTime(refund.getSuccessTime());
        wechatRefund.setCreateTime(refund.getCreateTime());
        wechatRefund.setChannel(refund.getChannel() != null ? refund.getChannel().name() : null);
        wechatRefund.setStatus(refund.getStatus() != null ? refund.getStatus().name() : null);

        Amount amount = refund.getAmount();
        wechatRefund.setTotal(amount.getTotal());
        wechatRefund.setRefund(amount.getRefund());
        wechatRefund.setPayerTotal(amount.getPayerTotal());
        wechatRefund.setPayerRefund(amount.getPayerRefund());
        wechatRefund.setSettlementRefund(amount.getSettlementRefund());
        wechatRefund.setSettlementTotal(amount.getSettlementTotal());
        wechatRefund.setDiscountRefund(amount.getDiscountRefund());
        wechatRefund.setRefundFee(amount.getRefundFee());
        return wechatRefund;
    }

    private void rollbackMembers(BizOrder order) {
        log.info("退款：开始回归会员权益，订单号：{}", order.getOrderNum());
        LocalDate now = LocalDate.now();
        Member member = memberMapper.selectById(order.getMemberId());
        if (member.getIsMembers().equals(SURE) && member.getMembersExpiryAt() != null &&
                !member.getMembersExpiryAt().isBefore(now)) {
            BizMembersRel rel = membersRelMapper.selectOne(new QueryWrapper<BizMembersRel>().lambda()
                    .eq(BizMembersRel::getBizOrderId, order.getId())
                    .ge(BizMembersRel::getEndAt, now));
            if (rel != null) {
                LocalDate start = rel.getStartAt().isBefore(now) ? now : rel.getStartAt();
                LocalDate end = rel.getEndAt();
                long between = ChronoUnit.DAYS.between(start, end) + 1;
                end = member.getMembersExpiryAt().minusDays(between);
                int update;
                redis.delete(MEMBER_INFO_KEY + member.getId());
                redis.delete(BIZ_USER_DETAIL_KEY + member.getId());
                if (end.isBefore(now)) {
                    update = memberMapper.update(null, new UpdateWrapper<Member>().lambda()
                            .eq(Member::getId, member.getId())
                            .eq(Member::getIsMembers, SURE)
                            .eq(Member::getMembersExpiryAt, member.getMembersExpiryAt())
                            .set(Member::getIsMembers, NEGATE)
                            .set(Member::getMembersExpiryAt, null));
                } else {
                    update = memberMapper.update(null, new UpdateWrapper<Member>().lambda()
                            .eq(Member::getId, member.getId())
                            .eq(Member::getIsMembers, SURE)
                            .eq(Member::getMembersExpiryAt, member.getMembersExpiryAt())
                            .set(Member::getIsMembers, SURE)
                            .set(Member::getMembersExpiryAt, end));
                }
                redis.delete(MEMBER_INFO_KEY + member.getId());
                redis.delete(BIZ_USER_DETAIL_KEY + member.getId());
                if (update < 1) {
                    rollbackMembers(order);
                }
                membersRelMapper.update(null, new UpdateWrapper<BizMembersRel>().lambda()
                        .eq(BizMembersRel::getBizOrderId, order.getId()).set(BizMembersRel::getIsExpired, NEGATE));
            }
        }
        log.info("退款：回归会员权益成功，订单号：{}", order.getOrderNum());
    }

    private void rollbackTrain(BizOrder order) {
        // todo 训练营回归还得再看下
        log.info("退款：开始回归训练营权益，订单号：{}", order.getOrderNum());
        Integer memberId = order.getMemberId();
        contentMemberRelMapper.delete(new QueryWrapper<BizCoreContentMemberRel>().lambda()
                .eq(BizCoreContentMemberRel::getBizOrderId, order.getId()));
        Set<Integer> classIds = classMapper.selectList(new QueryWrapper<BizTrainClass>().lambda()
                .eq(BizTrainClass::getBizTrainId, order.getGoodsId())).stream()
                .map(BizTrainClass::getId).collect(Collectors.toSet());
        if (!classIds.isEmpty()) {
            Long l = classMemberRelMapper.selectCount(new QueryWrapper<BizTrainClassMemberRel>().lambda()
                    .eq(BizTrainClassMemberRel::getBizTrainId, order.getGoodsId())
                    .ne(BizTrainClassMemberRel::getBizIdentityCode, STUDENT));
            if (l == null || l == 0) {
                classMemberRelMapper.delete(new QueryWrapper<BizTrainClassMemberRel>().lambda()
                        .eq(BizTrainClassMemberRel::getBizTrainId, order.getGoodsId())
                        .in(BizTrainClassMemberRel::getBizTrainClassId, classIds)
                        .eq(BizTrainClassMemberRel::getMemberId, memberId));
                classMemberManageMapper.delete(new QueryWrapper<BizTrainClassMemberManage>().lambda()
                        .in(BizTrainClassMemberManage::getBizTrainClassId, classIds)
                        .and(wrapper -> wrapper.eq(BizTrainClassMemberManage::getMemberId, memberId)
                                .or().eq(BizTrainClassMemberManage::getManageId, memberId)));
                classVoteMapper.delete(new QueryWrapper<BizTrainClassVote>().lambda()
                        .in(BizTrainClassVote::getBizTrainClassId, classIds)
                        .and(wrapper -> wrapper.eq(BizTrainClassVote::getCandidateId, memberId)
                                .or().eq(BizTrainClassVote::getVoterId, memberId)));
                homeworkCommentMapper.update(null, new UpdateWrapper<BizTrainHomeworkComment>().lambda()
                        .in(BizTrainHomeworkComment::getBizTrainClassId, classIds)
                        .and(wrapper -> wrapper.eq(BizTrainHomeworkComment::getMemberId, memberId)
                                .or().eq(BizTrainHomeworkComment::getCommenteeId, memberId))
                        .set(BizTrainHomeworkComment::getIsDeleted, SURE)
                        .set(BizTrainHomeworkComment::getDeleteAt, LocalDateTime.now()));
                Set<Integer> taskIds = taskMapper.selectList(new QueryWrapper<BizTrainTask>().lambda()
                                .in(BizTrainTask::getBizTrainClassId, classIds)).stream().map(BizTrainTask::getId)
                        .collect(Collectors.toSet());
                if (!taskIds.isEmpty()) {
                    taskMemberRelMapper.delete(new QueryWrapper<BizTrainTaskMemberRel>().lambda()
                            .in(BizTrainTaskMemberRel::getId, taskIds).eq(BizTrainTaskMemberRel::getMemberId, memberId));
                    moderatorMapper.delete(new QueryWrapper<BizTrainTaskModerator>().lambda()
                            .in(BizTrainTaskModerator::getBizTrainTaskId, taskIds).eq(BizTrainTaskModerator::getMemberId, memberId));
                }
            } else {
                classMemberRelMapper.delete(new QueryWrapper<BizTrainClassMemberRel>().lambda()
                        .eq(BizTrainClassMemberRel::getBizTrainId, order.getGoodsId())
                        .eq(BizTrainClassMemberRel::getBizIdentityCode, STUDENT)
                        .in(BizTrainClassMemberRel::getBizTrainClassId, classIds)
                        .eq(BizTrainClassMemberRel::getMemberId, memberId));
            }
        }
        log.info("退款：回归训练营权益成功，订单号：{}", order.getOrderNum());
    }

    private void rollbackCourse(BizOrder order) {
        log.info("退款：开始回归课程权益，订单号：{}", order.getOrderNum());
        Integer memberId = order.getMemberId();
        contentMemberRelMapper.delete(new QueryWrapper<BizCoreContentMemberRel>().lambda()
                .eq(BizCoreContentMemberRel::getBizOrderId, order.getId()));
        // 获取课程下，该用户发表的所有评论
        Set<Integer> commentIds = courseCommentMapper.selectList(new QueryWrapper<BizCourseComment>().lambda()
                        .eq(BizCourseComment::getBizCourseId, order.getGoodsId())
                        .eq(BizCourseComment::getMemberId, memberId))
                .stream().map(BizCourseComment::getId).collect(Collectors.toSet());
        if (!commentIds.isEmpty()) {
            courseCommentLikeMapper.delete(new QueryWrapper<BizCourseCommentLike>().lambda()
                    .in(BizCourseCommentLike::getBizCurseCommentId, commentIds));
            courseCommentMapper.update(null, new UpdateWrapper<BizCourseComment>().lambda()
                    .eq(BizCourseComment::getBizCourseId, order.getGoodsId())
                    .and(wrapper -> wrapper.eq(BizCourseComment::getMemberId, memberId)
                            .or()
                            .eq(BizCourseComment::getCommenteeId, memberId))
                    .set(BizCourseComment::getIsDeleted, SURE));
        }
        commentIds = courseCommentMapper.selectList(new QueryWrapper<BizCourseComment>().lambda()
                        .eq(BizCourseComment::getBizCourseId, order.getGoodsId()))
                .stream().map(BizCourseComment::getId).collect(Collectors.toSet());
        if (!commentIds.isEmpty()) {
            courseCommentLikeMapper.delete(new QueryWrapper<BizCourseCommentLike>().lambda()
                    .in(BizCourseCommentLike::getBizCurseCommentId, commentIds)
                    .eq(BizCourseCommentLike::getMemberId, memberId));
        }
        log.info("退款：回归课程权益成功，订单号：{}", order.getOrderNum());
    }
}
